package hw3.xhtml;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSParser;
import org.w3c.dom.ls.LSSerializer;

/**
 * This class is is an abstraction of an XHTML document. it allows taking an existing 
 * XHTML file (as a base file), change it and save it in a specific place.
 *  
 * @author Adam Levi
 * @author Marina Skarbovsky
 *
 */
public class XHTMLDocument
{
	/**
	 * creates an object of type XHTMLDocument
	 * @param file The file that will be used as a reference.
	 */
	public XHTMLDocument(String file) throws ClassCastException, ClassNotFoundException, InstantiationException, IllegalAccessException
	{
		DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
		DOMImplementationLS impl = (DOMImplementationLS)registry.getDOMImplementation("LS");
		LSParser builder = impl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
		m_Documnet = builder.parseURI(file);

	}
	
	/**
	 * sets some text in a specific element
	 * @param elementID The element ID
	 * @param text a string
	 */
	public void setText(
			String elementID,
			String text)
	{
		Element elem = m_Documnet.getElementById(elementID);
		elem.setTextContent(text);
	}
	
	/**
	 * sets a table with the given values
	 * @param tableID the table's ID
	 * @param header the table's header (column names)
	 * @param rows the table's rows
	 */
	public void setTable(
			String tableID,
			String[] header,
			ArrayList<Object[]> rows)
	{
		Element table = m_Documnet.getElementById(tableID);
		updateTableHeader(header, table);
		updateTableRows(rows, table);
	}
	
	/**
	 * saves the current document to a specific path
	 * @param path a path
	 */
	public void save(String path) throws IOException
	{
	
        DOMImplementationLS DOMiLS = (DOMImplementationLS)m_Documnet.getImplementation().getFeature("LS","3.0");

        //get a LSOutput object
        LSOutput LSO=DOMiLS.createLSOutput();

        //setting the location for storing the result of serialization
       	FileOutputStream  FOS = new FileOutputStream(path);
       	LSO.setByteStream(FOS);             

        //get a LSSerializer object
        LSSerializer LSS=DOMiLS.createLSSerializer();
        DOMConfiguration config = LSS.getDomConfig();

        config.setParameter("format-pretty-print", true);
        
	    //do the serialization
	    boolean res = LSS.write(m_Documnet, LSO);
	    
		if (!res)
		{
			throw new IOException();
		}
	}
	
	/**
	 * retrieves a table row from the given array
	 * @param values an array of objects that represent the row
	 * @return a node that will be used as a row in the document
	 */
	Node getNewTableRow(Object[] values)
	{
		int n = values.length;
		Node newRow = m_Documnet.createElement("tr");
		{
			for (int i = 0; i < n; i++)
			{
				Element newData = m_Documnet.createElement("td");
				newData.setTextContent(values[i].toString());
				newRow.appendChild(newData);
			}
		}
		return newRow;
	}
	
	/**
	 * updates the given table element with the given header
	 * @param header an array that represents the column names
	 * @param table a table element
	 */
	void updateTableHeader(String[] header, Element table)
	{
		Element tableHeaderRow = (Element)table.getElementsByTagName("thead").item(0);
		tableHeaderRow = (Element)tableHeaderRow.getElementsByTagName("tr").item(0);
		for (int i = 0; i < header.length; i++)
		{
			Element newData = m_Documnet.createElement("th");
			newData.setTextContent(header[i].toString());
			tableHeaderRow.appendChild(newData);
		}
	}
	
	/**
	 * updates the given table element with the given rows
	 * @param rows a list of rows
	 * @param table a table element
	 */
	void updateTableRows(ArrayList<Object[]> rows, Element table)
	{
		Node tableBody = table.getElementsByTagName("tbody").item(0);
		for (Object[] curArr : rows)
		{
			tableBody.appendChild(getNewTableRow(curArr));
		}
	}
	
	Document m_Documnet;;
	String m_CssFile;
}
